#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "green_returns.h"
#include "green_types.h"

#define BYTECODE_VERSION 1
#define POOL_SIZE 16

#pragma pack(push, 2)
typedef struct FH {//FileHeader
				uint16_t signature;
				uint32_t version;
				uint32_t constsAmmount;
				uint32_t constsSize;
				} Fileheader, PFileheader;
#pragma pack(pop)

PDSIResult dynamicStacksInterpreter(PContextStackListElement, unsigned char *, char **, PFunctions, size_t);

int main(int argc, char **argv)
{
	FILE *executable = NULL;
	int16_t mainID;
	uint32_t funcsAmmount, funcSize;
	size_t i, j, mainFunc;
	void *tmp;
	char **consts = NULL;
	char *constsPool = NULL, *stringBeginning = NULL, *funcBlock = NULL, *slider = NULL;
	char mainWasFind;
	Fileheader filehdr;
	PFunctions funcsSet = NULL;
	PContextStackListElement mainContext = NULL;
	PDSIResult dresult = NULL;
	
	if(argc < 2)
	{
		fprintf(stderr, "green!/ No executable file.\n");
		return NO_EXECUTABLE_FILE_I;
	}
	executable = fopen(argv[1], "rb");
	if(executable == NULL)
	{
		fprintf(stderr, "green!/ No such file.\n");
		return NO_SUCH_FILE_I;
	}
	fread(&filehdr, sizeof(Fileheader), 1, executable);
	fflush(stdout);
	if(filehdr.signature != 0xBABA)
	{
		fprintf(stderr, "green!/ Invalid signature of file %s.\n", argv[1]);
		return INVALID_SIGNATURE_OF_FILE_I;
	}
	if(filehdr.version > BYTECODE_VERSION)
	{
		fprintf(stderr, "green!/ This bytecode is not supported. Need version %u, instead of %u.\n", BYTECODE_VERSION, filehdr.version);
		return INVALID_VERSION_OF_BYTECODE_I;
	}
	consts = (char**) malloc((filehdr.constsAmmount + 1)*sizeof(char*));
	if(consts == NULL)
	{
		fprintf(stderr, "green!/ There is no free memory.\n");
		return OUT_OF_MEMORY_I;
	}
	constsPool = (char*) malloc((filehdr.constsSize)*sizeof(char));
	if(constsPool == NULL)
	{
		fprintf(stderr, "green!/ There is no free memory.\n");
		return OUT_OF_MEMORY_I;
	}
	fread(constsPool, filehdr.constsSize*sizeof(char), 1, executable);
	fread(&mainID, 2, 1, executable);
	fread(&funcsAmmount, 4, 1, executable);
	consts[0] = (char*) malloc(sizeof(char));
	consts[0][0] = '\0';
	stringBeginning = constsPool;
	slider = constsPool;
	for(i = 1; i <= filehdr.constsAmmount; i++)
	{ 
		while(*(slider++) != '\0');
		consts[i] = stringBeginning;
		stringBeginning = slider;
	}
	funcsSet = (PFunctions) malloc(funcsAmmount*sizeof(Functions));
	if(funcsSet == NULL)
	{
		fprintf(stderr, "green!/ There is no free memory.\n");
		return OUT_OF_MEMORY_I;
	}
	mainWasFind = 0;
	for(i = 0; i < funcsAmmount; i++)
	{
		fread(&funcSize, 4, 1, executable);
		funcBlock = (char*) malloc(funcSize*sizeof(char));
		if(funcBlock == NULL)
		{
			fprintf(stderr, "green!/ There is no free memory.\n");
			return OUT_OF_MEMORY_I;
		}
		fread(funcBlock, funcSize*sizeof(char), 1, executable);
		slider = funcBlock;
		funcsSet[i].bytecodeSize = *((Quantity*) slider);
		slider += sizeof(Quantity);
		funcsSet[i].signature = (char*) malloc( (*((Quantity*) slider))*sizeof(char) );
		if(funcsSet[i].signature == NULL)
		{
			fprintf(stderr, "green!/ There is no free memory.\n");
			return OUT_OF_MEMORY_I;
		}
		memcpy(funcsSet[i].signature, slider + sizeof(Quantity), *((Quantity*) slider));
		slider += sizeof(Quantity) + *((Quantity*) slider);
		funcsSet[i].id = *((FunctionID*) slider);
		if( *((FunctionID*) slider) == mainID )
		{
			mainFunc = i;
			mainWasFind = 1;
		}
		slider += sizeof(FunctionID);
		funcsSet[i].varAmmount = *((Quantity*) slider);
		slider += sizeof(Quantity);
		funcsSet[i].argAmmount = *((Quantity*) slider);
		slider += sizeof(Quantity);
		funcsSet[i].bytecode = (char*) malloc( (*((Quantity*) slider))*sizeof(char) );
		if(funcsSet[i].bytecode == NULL)
		{
			fprintf(stderr, "green!/ There is no free memory.\n");
			return OUT_OF_MEMORY_I;
		}
		memcpy(funcsSet[i].bytecode, slider, funcsSet[i].bytecodeSize);
		free(funcBlock);
	}
	if(!mainWasFind)
	{
		fprintf(stderr, "green!/ There is no such function.");
		return NO_SUCH_FUNCTION_I;
	}
	mainContext = (PContextStackListElement) malloc(sizeof(ContextStackListElement));
	if(mainContext == NULL)
	{
		fprintf(stderr, "green!/ There is no free memory.\n");
		return OUT_OF_MEMORY_I;
	}
	mainContext->nextElement = NULL;
	mainContext->variables = (PGeneralStackElement) malloc(funcsSet[mainFunc].varAmmount*sizeof(GeneralStackElement));
	if(mainContext->variables == NULL)
	{
		fprintf(stderr, "green!/ There is no free memory.\n");
		return OUT_OF_MEMORY_I;
	}
	mainContext->varAmmount = funcsSet[mainFunc].varAmmount;
	mainContext->argAmmount = funcsSet[mainFunc].argAmmount;	
	mainContext->id = funcsSet[mainFunc].id;
	
	dresult = dynamicStacksInterpreter(mainContext, funcsSet[mainFunc].bytecode, consts, funcsSet, POOL_SIZE);
	
	fprintf(stderr, "\n");
	while(dresult->generalStackPoolsSet != NULL)
	{
		free(dresult->generalStackPoolsSet->pool);
		tmp = (void*) dresult->generalStackPoolsSet->nextPool;
		dresult->generalStackPoolsSet = (PGeneralStackPools) tmp;
		free(tmp);
	}
	switch(dresult->retcode)
	{
		case OUT_OF_MEMORY_I:fprintf(stderr, "green!/ There is no free memory. Interpretation was stoped.\n");
							return OUT_OF_MEMORY_I;
		case INVALID_I:fprintf(stderr, "green-/ INVALID instruction has been met.\n");
					return INVALID_I;
		case OUT_OF_MAIN_CICLE_I:fprintf(stderr, "green!/ Interpretation ended out of infinite loop.\n");
								return OUT_OF_MAIN_CICLE_I;
		case INDEX_OUT_OF_BOUND_I:fprintf(stderr, "green!/ There is no such variable. Interpretation was stoped.\n");
								return INDEX_OUT_OF_BOUND_I;
		case STOPED_I:fprintf(stderr, "green-/ STOP instruction has been met.\n");
					return STOPED_I;
		case NO_SUCH_CONTEXT_I:fprintf(stderr, "green!/ This function hasn't been started. Interpretation was stoped.\n");
							return NO_SUCH_CONTEXT_I;
		case NO_SUCH_FUNCTION_I:fprintf(stderr, "green!/ There is no such function. Interpretation was stoped.\n");
								return NO_SUCH_FUNCTION_I;
		case 0:return 0;
	}
}
